//
//  Beep.m
//  HelloIphone
//
//  Created by Geoffrey Gallaway on 12/17/08.
//

#include <AudioToolbox/AudioToolbox.h>
#import "Beep.h"

#define NUM_BUFFERS 3

typedef struct AQCallbackStruct {
	AudioQueueRef					queue; 
	UInt32							frameCount; 
	AudioQueueBufferRef				mBuffers[NUM_BUFFERS]; 
	AudioStreamBasicDescription		mDataFormat;
	UInt32							frequency;
	UInt32							sampleNr;
	bool							isDonePlaying;
	void *   						testBuffer;
	NSMutableArray					*tones;
	Tone							*tone;
	int								currentToneNumber;
	bool							playbackComplete;
} AQCallbackStruct; 

AQCallbackStruct in;

//int sampleNr = 0;
/*
 * The callback for AudioQueue that generates the beep.
 * Fills the buffer's mAudioData element with a simple beep sound?
*/
static void playbackCallback (void *inUserData, AudioQueueRef inAudioQueue, AudioQueueBufferRef bufferReference) {
	// Assignment of the structure and the pointer for the output buffer
	AQCallbackStruct *inData = (AQCallbackStruct *)inUserData;

	NSLog(@"instance: %d", bufferReference->mAudioDataBytesCapacity);
	short *coreAudioBuffer = (short*) bufferReference->mAudioData;
	
	if (inData->isDonePlaying) {
		NSLog(@"STOPPED");
		AudioQueueFlush(inAudioQueue);
		OSStatus error = AudioQueueStop(inAudioQueue, false);
		inData->playbackComplete = true;
		if (error) NSLog(@"AudioQueueStop failed with %d", (int)error);
		inData->sampleNr = 0;
		return;
	}
	
	// Fill up the audio buffer with the tones.
	int totalTones = [inData->tones count];
	
	Tone *currentTone = [inData->tones objectAtIndex:inData->currentToneNumber];
	int loopIter = 0;
	for (loopIter = 0; loopIter < bufferReference->mAudioDataBytesCapacity / 2; loopIter = loopIter + 2) {
		// Grab a sample from the current tone, shove it into the buffer.
		if ([currentTone hasNextSample]) {
			float sampleValue = [currentTone getNextSample];
			coreAudioBuffer[loopIter] = sampleValue;
		// This tone is out of samples, are there more tones?
		} else if (inData->currentToneNumber < totalTones - 1) {
			NSLog(@"No more samples available");
			currentTone = [inData->tones objectAtIndex:++inData->currentToneNumber];
			float sampleValue = [currentTone getNextSample];
			coreAudioBuffer[loopIter] = sampleValue;
		// There are no more samples in any of the tones, we're done.
		} else {
			NSLog(@"No more tones available");
			inData->isDonePlaying = true;
			break;
		}
	}
	
	// Tell the audio system how many bytes of valid data are in the buffer
	bufferReference->mAudioDataByteSize = sizeof(int) * loopIter;
	OSStatus error = AudioQueueEnqueueBuffer(inAudioQueue, bufferReference, 0, NULL);
	if (error) NSLog(@"AudioQueueEnqueueBuffer failed with %d", (int)error);		
}

// property callback function, invoked when a property changes. 
static void propertyListenerCallback (void *inUserData,AudioQueueRef queueObject,AudioQueuePropertyID propertyID) {
	Beep *player = (Beep *) inUserData;
	
	if (player.audioPlayerShouldStopImmediately == YES) {
		[player.notificationDelegate updateUserInterfaceOnAudioQueueStateChange: player];
	} else {
		[player.notificationDelegate performSelector: @selector (updateUserInterfaceOnAudioQueueStateChange:) withObject: player afterDelay: 0];
	}
}
							  

@implementation Beep

@synthesize numPacketsToRead;
@synthesize donePlayingFile;
@synthesize audioPlayerShouldStopImmediately;
@synthesize bufferByteSize;
@synthesize gain;

-(id)init {
	
	self = [super init];
	if (self != nil) {
		[self setupAudioPlayback];
		//[self setupPlaybackAudioQueueObject];
		//[self setDonePlayingFile: NO];
		//[self setAudioPlayerShouldStopImmediately: NO];
	}
	return self;
}


- (void) setupAudioQueueBuffers {

}

- (void) setupAudioPlayback {
	//AQCallbackStruct in; 
	in.mDataFormat.mSampleRate = 44100.0; 
	in.mDataFormat.mFormatID = kAudioFormatLinearPCM;
	in.mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; //| kAudioFormatFlagIsPacked; 
	in.mDataFormat.mBytesPerPacket = 8;
	in.mDataFormat.mFramesPerPacket = 1;
	in.mDataFormat.mBytesPerFrame = 4;
	in.mDataFormat.mChannelsPerFrame = 2;
	in.mDataFormat.mBitsPerChannel = 16;
	in.frameCount = 1024;
	in.sampleNr = 0;
	in.isDonePlaying = true;
	in.playbackComplete = true;
	in.tones = [[NSMutableArray alloc] init];
	
	// create the playback audio queue object
	OSStatus error = AudioQueueNewOutput(&in.mDataFormat,playbackCallback,&in,CFRunLoopGetCurrent(),kCFRunLoopCommonModes,0,&in.queue);
	if (error) NSLog(@"AudioQueueNewOutput failed with %d", (int)error);
	
	bufferByteSize = in.frameCount * in.mDataFormat.mBytesPerFrame;
	
	AudioQueueSetParameter(in.queue, kAudioQueueParam_Volume, .50);
	if (error) NSLog(@"AudioQueueSetParameter failed with %d", (int)error);
	for (int bufferIndex = 0; bufferIndex < NUM_BUFFERS; bufferIndex++) {
		OSStatus error = AudioQueueAllocateBuffer(in.queue,sizeof(int) * 16384, &in.mBuffers[bufferIndex]);
		if (error) NSLog(@"AudioQueueAllocateBuffer failed with %d", (int)error);
	}
}

-(void)play {
	in.currentToneNumber = 0;
	in.isDonePlaying = false;
	in.playbackComplete = false;
	for (int bufferIndex = 0; bufferIndex < NUM_BUFFERS; bufferIndex++) {
		playbackCallback(&in, in.queue, in.mBuffers[bufferIndex]);
	}
	NSLog(@"Starting...");
	OSStatus error = AudioQueueStart(in.queue, NULL);
	if (error) NSLog(@"AudioQueueStart failed with %d", (int)error);
	CFRunLoopRunInMode(kCFRunLoopDefaultMode, 2, false);
}

-(bool)isDonePlaying {
	return CFRunLoopIsWaiting(CFRunLoopGetCurrent());
}

-(void)addTones: (NSArray*)newTones {
  [in.tones addObjectsFromArray:newTones];
}

-(void)addTone: (Tone*)tone {
	[in.tones addObject:tone];
}

-(void)removeTones {
	[in.tones removeAllObjects];
}


- (void) dealloc {
	AudioQueueDispose(queueObject, YES);
	[super dealloc];
}
@end
